#include "Sprite.h"

#define SPRITE_MAX(a,b)	((a > b) ? a : b)
#define SPRITE_MIN(a,b)	((a < b) ? a : b)

#define FLIP_HORIZONTAL 1
#define FLIP_VERTICAL   2

Sprite::Sprite()
{
	m_name=0;
	m_flipState=VSDL_FLIP_NONE;
	m_frameIndex=0;
	m_animationIndex=0;
	m_running=false;
	m_runningInReverse=false;
	m_loopsToBeginning = true;
	m_timer=0;
	m_surfaces.init();
}

void Sprite::release()
{
	for(int i = 0; i < m_animations.size(); ++i)
		m_animations.get(i).release();
	m_animations.release();
//	for(int s = 0; s < m_surfaces.size(); ++s)
//		m_surfaces.get(s)->release();
//		SDL_FreeSurface(m_surfaces.get(s));
	m_surfaces.release();
	m_imageClips.release();
	if(m_name)
	{
		delete m_name;
		m_name = 0;
	}
}

//Sprite::Sprite(const char * file, const int & a_frames, const int & a_speed)
//{
//    SDL_Surface *temp = SDL_LoadBMP(file);
//    m_surfaces = new SDL_Surface * [1];
//	m_surfaces[0] = SDL_DisplayFormat(temp);
//    SDL_FreeSurface(temp);
//	m_flipState = VSDL_FLIP_NONE;
//	restart();
//    m_loopsToBeginning = true;
//
//	m_numAnimations = 1;
//	m_animations = new SAnimation[m_numAnimations];
//	m_animations[0].setFromStrip(m_surfaces[0], a_frames, a_speed);
//	m_running = m_animations[0].getFrameCount() > 0;
//	m_runningInReverse = false;
//}

//Sprite::Sprite(const SDL_Surface* a_surface, const int & a_frames, const int & a_speed) {
//	if(a_surface == NULL) {
//		m_surfaces = NULL;
//	} else {
//		SDL_PixelFormat * f = a_surface->format;
//		m_surfaces = new SDL_Surface * [1];
//		// create a new surface
//		m_surfaces[0] = SDL_CreateRGBSurfaceFrom(
//			a_surface->pixels, a_surface->w, a_surface->h, f->BitsPerPixel,
//			a_surface->pitch, f->Rmask, f->Gmask, f->Bmask,
//				(a_surface->flags & SDL_SRCCOLORKEY)?0:f->Amask);
//		if(a_surface->flags & SDL_SRCCOLORKEY) {
//			SDL_SetColorKey(m_surfaces[0], SDL_RLEACCEL|SDL_SRCCOLORKEY, f->colorkey);
//		}
//	}
//	m_flipState = VSDL_FLIP_NONE;
//	restart();
//	m_loopsToBeginning = true;
//
//	m_numAnimations = 1;
//	m_animations = new SAnimation[m_numAnimations];
//	m_animations[0].setFromStrip(m_surfaces[0], a_frames, a_speed);
//	m_running = m_animations[0].getFrameCount() > 0;
//}

//Sprite::Sprite(const char * a_filename, const short * a_data)
//{
//	m_name=0;
//	m_surfaces.allocateToSize(1);
//	m_surfaces.set(0, BitMaP::load(a_filename));
//	set(&m_surfaces, a_data);
//}

Sprite::Sprite(const char * a_spriteName, const TemplateArray<BitMaP*> * a_images, const short * a_data)
{
	m_name=0;
	set(a_spriteName, a_images, a_data);
}

void Sprite::setName(const char * a_spriteName)
{
	if(m_name)
		delete m_name;
	int nameLen = strlen(a_spriteName);
	m_name = new char[nameLen+1];
	strcpy_s(m_name, nameLen+1, a_spriteName);
}
void Sprite::set(const char * a_spriteName, const TemplateArray<BitMaP*> * a_images, const short * a_data)
{
	setName(a_spriteName);
	set(a_images, a_data);
}
void Sprite::set(const TemplateArray<BitMaP*> * a_images, const short * a_data)
{
	m_flipState=VSDL_FLIP_NONE;
	m_frameIndex=0;
	m_animationIndex=0;
	m_running=false;
	m_runningInReverse=false;
	m_loopsToBeginning = true;
	m_timer=0;
	if(a_images && a_images != &m_surfaces)	// don't bother copying yourself...
		m_surfaces.copy(*a_images);
	int index = 0;

	int numImageSlotsNeeded = 0;
	m_imageClips.allocateToSize(a_data[index++]);
	for(int i = 0; i < m_imageClips.size(); ++i)
	{
		if(a_data[index+0]+1 > numImageSlotsNeeded)
			numImageSlotsNeeded = a_data[index+0]+1;
		m_imageClips.get(i).set(
			(a_images && a_images->size() > a_data[index+0])
				?a_images->get(a_data[index+0])
				:(BitMaP*)a_data[index+0],
			SRect(a_data[index+1],
			a_data[index+2],
			a_data[index+3],
			a_data[index+4]));
		index += 5;
	}
	if(!a_images)
	{
		for(int i = 0; i < numImageSlotsNeeded; ++i)
			m_surfaces.add((BitMaP*)i);
	}

	SAnimation * sa;
	SFrame * sf;
	SClip * sc;
	//m_numAnimations = a_data[index++];
	//m_animations = new SAnimation[m_numAnimations];
	m_animations.allocateToSize(a_data[index++]);
	for(int a = 0; a < m_animations.size(); ++a)
	{
		sa = &(m_animations.get(a));
		// TODO move into SAnimation
		sa->getFrames()->allocateToSize(a_data[index++]);
		for(int f = 0; f < sa->getFrames()->size(); ++f)
		{
			sf = &(sa->getFrames()->get(f));
			// TODO move into SFrames
			sf->setDuration(a_data[index++]);
			sf->getClips()->allocateToSize(a_data[index++]);
			for(int c = 0; c < sf->getClips()->size(); ++c)
			{
				sc = &(sf->getClips()->get(c));
				// TODO move into SClip
				sc->set(&m_imageClips.get(a_data[index+0]),
					SPoint(a_data[index+1], a_data[index+2]));
				index += 3;
			}
		}
	}
	m_running = m_animations.size() > 0;
}

int Sprite::get_SDL_FLIP_state()
{
	return m_flipState;
}
void Sprite::set_SDL_FLIP_state(const int & a_flipState)
{
	m_flipState = a_flipState;
}

void Sprite::draw(SDL_Surface* a_screen, const SPoint & a_point)
{
	m_animations.get(m_animationIndex).draw(a_screen, a_point, 
		m_frameIndex, m_flipState, 1);
}

void Sprite::draw(SDL_Surface* a_screen, const SPoint & a_point, const int & a_zoom)
{
	m_animations.get(m_animationIndex).draw(a_screen, a_point, 
		m_frameIndex, m_flipState, a_zoom);
}

Sprite::~Sprite() {
	release();
}

void Sprite::start() {
	m_running = true;
}

void Sprite::restart() {
	m_frameIndex = 0;
	m_timer = 0;
}

/**
 * @param a_msPassed the number of milliseconds since the last time update was called 
 * @return true if the sprite state changed
 */
bool Sprite::update(const int & a_msPassed) {
	bool changed = false;
	if(m_running)
	{
		m_timer += a_msPassed;
		Uint32 speed = m_animations.get(m_animationIndex).getFrameDelay(m_frameIndex);
		if(m_timer > speed)
		{
			m_timer -= speed;
			if(!m_runningInReverse) {
				++m_frameIndex;
			} else {
				--m_frameIndex;
			}
			if(m_frameIndex >= m_animations.get(m_animationIndex).getFrameCount()) {
				m_frameIndex = 0;
			} else if(m_frameIndex < 0) {
				m_frameIndex = m_animations.get(m_animationIndex).getFrameCount()-1;
			}
			changed = true;
		}
	}
	return changed;
}

void Sprite::setLoopToBegin(bool loop) {
	m_loopsToBeginning = loop;
}

bool Sprite::running() const {
	return m_running;
}

void Sprite::stop() {
	m_running = false;
}

bool Sprite::isSprite() const {
	return m_animations.size() > 0 
		&& m_animationIndex < m_animations.size()
		&& m_animations.get(m_animationIndex).getFrameCount() > 0;
}

/*
 ***********************************************************
 Static functions to cut down on memory usage
 *********************************************************** */

//SDL_Surface* Sprite::getRect(const Sprite & sprite, const int & x, const int & y, const int & w, const int & h) {
//	SDL_Surface* newrect;
//	// create a new surface
//	SDL_PixelFormat * f = sprite.getSurface()->format;
//	if(sprite.getSurface()->flags & SDL_SRCCOLORKEY) {
//		newrect = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, f->BitsPerPixel,
//		f->Rmask, f->Gmask, f->Bmask, f->Amask );
//	} else {
//		newrect = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, f->BitsPerPixel,
//			f->Rmask, f->Gmask, f->Bmask, 0 );
//	}
//
//	for(int j = 0; j < h; j++) {
//		for(int i = 0; i < w; i++) {
//			setPixel(newrect, SPoint(i, j), sprite.getPixel(SPoint(x+i,y+j)));
//		}
//	}
//	//Copy color key
//	if(sprite.getSurface()->flags & SDL_SRCCOLORKEY) {
//		SDL_SetColorKey(newrect, SDL_RLEACCEL|SDL_SRCCOLORKEY, f->colorkey );
//	}
//	return newrect;
//}
//
//void Sprite::stretchX(Sprite & sprite, const float & stretchX) {
//    return stretch(sprite, stretchX, 100);
//}
//
//void Sprite::stretchY(Sprite & sprite, const float & stretchY) {
//    return stretch(sprite, 100, stretchY);
//}
//
//void Sprite::zoom(Sprite & sprite, const float & zoom) {
//    return stretch(sprite, zoom,zoom);
//}
//
//
//void Sprite::stretch(Sprite & sprite, float stretchX, float stretchY) {
//	SDL_Surface* zoomed;
//	if(stretchX < 1 || stretchY < 1) {
//		return;
//	}
//	stretchX /= 100;
//	stretchY /= 100;
//	SDL_PixelFormat * f = sprite.getSurface()->format;
//	// create a new surface
//	if(sprite.getSurface()->flags & SDL_SRCCOLORKEY) {
//		zoomed = SDL_CreateRGBSurface(SDL_SWSURFACE, 
//			(int)(sprite.getSurface()->w*stretchX), 
//			(int)(sprite.getSurface()->h*stretchY), 
//		f->BitsPerPixel, f->Rmask, f->Gmask, f->Bmask, f->Amask  );
//	} else {
//		zoomed = SDL_CreateRGBSurface(SDL_SWSURFACE, 
//			(int)(sprite.getSurface()->w*stretchX), 
//			(int)(sprite.getSurface()->h*stretchY), 
//		f->BitsPerPixel, f->Rmask, f->Gmask, f->Bmask, 0);
//	}
//	if(SDL_MUSTLOCK(sprite.getSurface())) {
//		SDL_LockSurface(sprite.getSurface());
//	}
//	int zoomedWidth = zoomed->w/sprite.m_maxFrames;
//
//	for(int y = 0; y < zoomed->h; y++) {
//		for(int x = 0; x < zoomedWidth; x++) {
//		// iterate over each animation as opposed to the whole sprite, to ensure that each animation is resized properly
//			for(int i = 0; i < sprite.m_maxFrames; i++) {
//			setPixel(zoomed, SPoint((zoomedWidth * i) + x, y), sprite.getPixel(SPoint((sprite.getWidth() * i) + (int)(x/stretchX),(int)(y/stretchY)) ));
//			}
//		}
//	}
//	sprite.m_size.set(zoomedWidth, zoomed->h);
//	if(sprite.getSurface()->flags & SDL_SRCCOLORKEY ) {
//		SDL_SetColorKey(zoomed, SDL_RLEACCEL|SDL_SRCCOLORKEY, f->colorkey );
//	}
//	if(SDL_MUSTLOCK(sprite.getSurface())) {
//		SDL_UnlockSurface(sprite.getSurface());
//	}
//	SDL_FreeSurface(sprite.getSurface());
//	sprite.setSurface(zoomed);
//}

//void Sprite::setPixel(SDL_Surface* sprite, const SPoint & a, const Uint32 & pixel) {
//	int bpp = sprite->format->BytesPerPixel;
//	/* p is the address to the pixel we want to set */
//	Uint8 *p = (Uint8 *)sprite->pixels + a.y * sprite->pitch + a.x * bpp;
//	switch(bpp) {
//	case 1:
//		*p = pixel;
//		break;
//	case 2:
//		*(Uint16 *)p = pixel;
//		break;
//	case 3:
//		if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
//			p[0] = (pixel >> 16) & 0xff;
//			p[1] = (pixel >> 8) & 0xff;
//			p[2] = pixel & 0xff;
//		} else {
//			p[0] = pixel & 0xff;
//			p[1] = (pixel >> 8) & 0xff;
//			p[2] = (pixel >> 16) & 0xff;
//		}
//		break;
//	case 4:
//		*(Uint32 *)p = pixel;
//		break;
//	}
//}
//
//Uint32 Sprite::getPixel(const SDL_Surface * a_surface, const SPoint & a) {
//	int bpp = a_surface->format->BytesPerPixel;
//	/* p is the address to the pixel we want to retrieve */
//	Uint8 *p = (Uint8 *)a_surface->pixels + a.y * a_surface->pitch + a.x * bpp;
//	switch(bpp) {
//	case 1:
//		return *p;
//	case 2:
//		return *(Uint16 *)p;
//	case 3:
//		if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
//			return p[0] << 16 | p[1] << 8 | p[2];
//		} else {
//			return p[0] | p[1] << 8 | p[2] << 16;
//		}
//		break;
//	case 4:
//		return *(Uint32 *)p;
//	default:
//		return 0;
//	}
//}
